/*
 * MIT License
 *
 * Copyright (c) 2023 芊芊墨客
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package com.zhangmeng.tools.controller;

import com.zhangmeng.tools.utils.AlertUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.Base64Codec;
import io.jsonwebtoken.impl.crypto.EllipticCurveSigner;
import io.jsonwebtoken.impl.crypto.MacSigner;
import io.jsonwebtoken.impl.crypto.RsaSigner;
import io.jsonwebtoken.impl.crypto.Signer;
import io.jsonwebtoken.lang.Assert;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.util.Callback;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static io.jsonwebtoken.SignatureAlgorithm.*;

/**
 * @author : 芊芊墨客
 * @version : 1.0
 * @date : 2023-02-18 16:21
 */
@Slf4j
public class JwtController {

    public static ObservableList<Data> list = FXCollections.observableArrayList();

    @FXML
    private TextField secret_key;

    @FXML
    private TextField key;

    @FXML
    private TextField value;

    @FXML
    private TextField expiration_time;

    @FXML
    private ComboBox<SignatureAlgorithm> comboBox;

    @FXML
    private ListView<Data> listView;

    @FXML
    private TextArea token;

    private volatile Key KEY = null;

    @FXML
    public void initialize() {
        ObservableList<SignatureAlgorithm> list_1 = FXCollections.observableArrayList();
        list_1.add(HS256);
        list_1.add(HS384);
        list_1.add(HS512);

        comboBox.setItems(list_1);
        comboBox.getSelectionModel().select(HS256);

        listView.setPlaceholder(new Label("没有数据"));
        listView.setItems(list);
        listView.getSelectionModel().select(0);
        listView.setFixedCellSize(40);
        listView.setCellFactory(new Callback<>() {
            @Override
            public ListCell<Data> call(ListView<Data> dataListView) {
                return new ListCell<>() {
                    @Override
                    protected void updateItem(Data data, boolean b) {
                        super.updateItem(data, b);
                        if (!b) {
                            HBox hBox = new HBox(15);
                            hBox.setAlignment(Pos.CENTER_RIGHT);
                            Label label = new Label(data.key + " -- " + data.value);
                            Button button = new Button("删除");
                            hBox.getChildren().addAll(label, button);
                            this.setGraphic(hBox);
                            button.setOnAction(event -> {
                                log.info("删除....");
                                list.remove(data);
                            });
                        }
                    }
                };
            }
        });

        token.setWrapText(true);
    }

    @FXML
    public void add() {
        log.info("添加...");
        if (key.getText().length() == 0) {
            AlertUtils.alert_warning("key不能为空");
            return;
        }
        if (value.getText().length() == 0) {
            AlertUtils.alert_warning("value不能为空");
            return;
        }
        Data data = new Data(key.getText(), value.getText());
        list.add(data);
        //清空
        clear_input(key);
        clear_input(value);
    }

    public void clear_input(TextField textField){
        textField.setText(null);
    }

    @FXML
    public void gen_jwt_token() {
        String secret;
        if (secret_key.getText().length() == 0) {
            if (key.getText().length() == 0) {
                AlertUtils.alert_warning("key不能为空");
                return;
            }
        }
        secret = secret_key.getText();
        Map<String, Object> map = new HashMap<>();
        list.forEach(i -> {
            map.put(i.key, i.value);
        });
        log.info("map:{}", map);
        long expiration = 0;
        if (expiration_time.getText().length() != 0) {
            expiration = System.currentTimeMillis() + Long.parseLong(expiration_time.getText());
        }
        SignatureAlgorithm signatureAlgorithm = comboBox.getSelectionModel().getSelectedItem();
        String res = generateToken(map, secret, expiration, signatureAlgorithm);
        log.info("token:{}", res);
        token.setText(res);
    }

    private String generateToken(Map<String, Object> claims, String secret, long expiration, SignatureAlgorithm signatureAlgorithm) {
        Date expirationDate = new Date(System.currentTimeMillis() + expiration);
        return switch (signatureAlgorithm){
            case HS256, HS384, HS512 -> Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(signatureAlgorithm, getKeyInstance(secret,signatureAlgorithm)).compact();
            case RS256, RS384, RS512, PS256, PS384, PS512 ->  "暂不支持,敬请期待!";
            case ES256, ES384, ES512 -> "暂不支持,敬请期待!";
            default -> throw new IllegalStateException("Unexpected value: " + signatureAlgorithm);
        };
    }

    public Key getKeyInstance(String secret,SignatureAlgorithm signatureAlgorithm) {
        if (KEY == null) {
            synchronized (JwtController.class) {
                // 双重锁
                if (KEY == null) {
                    byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(secret);
                    KEY = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
                }
            }
        }
        return KEY;
    }

    private Claims getClaimsFromToken(String token, String secret) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }

    public Boolean isTokenExpired(String token, String secret) {
        try {
            Claims claims = getClaimsFromToken(token, secret);
            Date expiration = claims.getExpiration();
            return expiration.before(new Date());
        } catch (Exception e) {
            return false;
        }
    }

    static class Data {

        private String key;
        private Object value;

        public Data(String key, Object value) {
            this.key = key;
            this.value = value;
        }

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public Object getValue() {
            return value;
        }

        public void setValue(Object value) {
            this.value = value;
        }

        public Data() {
        }
    }
}
